﻿//========================================================= 
/**@file TC_FingerDevAdapter.h
 * @brief 天诚指纹仪驱动
 * 
 * @date 2016-07-19   15:42:53
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_TC_FINGERDEVADAPTER_H_
#define _LIBZHOUYB_TC_FINGERDEVADAPTER_H_
//--------------------------------------------------------- 
#include "../../include/Base.h"
#include "../../include/Extension.h"
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace finger {
//--------------------------------------------------------- 
/// 天诚指纹仪驱动
class TC_FingerDevAdapter :
    public DevAdapterBehavior<IInteractiveTrans>,
    public RefObject
{
public:
    //----------------------------------------------------- 
    /// 常量
    enum TC_CONST_VALUE
    {
        /// 闪存参数
        FLSH_PLEN = 8,
        /// 配置长度
        CFG_LEN = 11,
        /// 图像宽度
        FPR_WIDE = 240,
        /// 指纹数据长度
        MAX_FIN_SIZE = 256,
        /// 图像高度
        FPR_HIGH = 272
    };
    /// 错误码
    enum TCR_ERR
    {
        /// 成功操作
        TCR_SUCC = 0x00,
        /// 失败结果
        TCR_FAIL = 0x01,
        /// 校验错误
        TCR_ERRO = 0x02,
        /// 操作超时
        TCR_TMOT = 0x03,
        /// 未连光头
        TCR_NODV = 0x04,
        /// 写闪存错
        TCR_FLSH = 0x05,
        /// 未按或孬
        TCR_NOFP = 0x06,
        /// 值不相关
        TCR_NOSM = 0x07,
        /// 值不匹配
        TCR_NOMT = 0x08,
        /// 库链为空
        TCR_EPLB = 0x09,
        /// 请抬起手
        TCR_TOLV = 0x0A,
        /// 内存不足
        TCR_NMEM = 0x0B,
        /// 不支持令
        TCR_NSUP = 0x0C,
        /// 特点过少
        TCR_LESS = 0x0D,
        /// 取消操作
        TCR_CNCL = 0x0E,
        /// 设备正忙
        TCR_BUSY = 0xFF
    };
    /// 指令代码
    enum ORD_CODE
    {
        /// 专用指令
        ORD_SPEC = 0x00,
        /// 内核信息
        ORD_INFO = 0x61,
        /// 设波特率
        ORD_BAUD = 0x62,
        /// 注册指纹
        ORD_REGI = 0x63,
        /// 库中比对
        ORD_VERI = 0x64,
        /// 读闪存区
        ORD_FLSR = 0x65,
        /// 写闪存区
        ORD_FLSW = 0x66,
        /// 访问数据
        ORD_DITO = 0x67,
        /// 连接测试
        ORD_LINK = 0x68,
        /// 特征分块
        ORD_STEP = 0x69,
        /// 友好提示
        ORD_HINT = 0x6A,
        /// 闪存配置
        ORD_FCFG = 0x6B,
        /// 读寄存器
        ORD_RREG = 0x6C,
        /// 写寄存器
        ORD_WREG = 0x6D,
        /// 系统配置
        ORD_SCFG = 0x71,
        /// 重新加载
        ORD_LOAD = 0x72,
        /// 强度测试
        ORD_HARD = 0x73,
        /// 背光蜂鸣
        ORD_CTRL = 0x74,
        /// 软件复位
        ORD_RSTX = 0x75,
        /// 标定UPEK
        ORD_CBRT = 0x76,
        /// 抓拍图像
        ORD_CAPT = 0x80,
        /// 置图当前
        ORD_PIMG = 0x81,
        /// 当前特征
        ORD_MNUT = 0x82,
        /// 比对指纹
        ORD_MTCH = 0x83,
        /// 分次注册
        ORD_RXGT = 0x91,
        /// 液晶语音
        ORD_OPAY = 0x92,
        /// 显示串儿
        ORD_STRS = 0x93,
        /// 启动键盘
        ORD_KBGN = 0x94,
        /// 键盘缓冲
        ORD_KBUF = 0x95
    };
    //----------------------------------------------------- 
    /// 将错误码转为字符串描述
    static const char* TransErrToString(TCR_ERR err)
    {
        switch(err)
        {
        case TCR_SUCC:
            return "成功操作";
        case TCR_FAIL:
            return "失败结果";
        case TCR_ERRO:
            return "校验错误";
        case TCR_TMOT:
            return "操作超时";
        case TCR_NODV:
            return "未连光头";
        case TCR_FLSH:
            return "写闪存错";
        case TCR_NOFP:
            return "未按或孬";
        case TCR_NOSM:
            return "值不相关";
        case TCR_NOMT:
            return "值不匹配";
        case TCR_TOLV:
            return "请抬起手";
        case TCR_NMEM:
            return "内存不足";
        case TCR_NSUP:
            return "不支持令";
        case TCR_LESS:
            return "特点过少";
        case TCR_CNCL:
            return "取消操作";
        case TCR_BUSY:
            return "设备正忙";
        case TCR_EPLB:
            return "库链为空";
        }
        return "";
    }
    //----------------------------------------------------- 
    /// 组包指令
    static void PackSendCmd(byte cmd, const ByteArray& data, bool isExpand, ByteBuilder& sCmd)
    {
        const byte HDR = 0x7E;
        const byte FMT_BIN = 0x42;
        const byte FMT_STR = 0x53;

        sCmd += HDR;

        ByteBuilder cmdBuff(8);
        cmdBuff += cmd;
        ByteConvert::FromObject(data.GetLength(), cmdBuff, 4);
        cmdBuff += data;

        byte chk = ByteConvert::XorVal(cmdBuff);
        // 需要拆分数据
        if(isExpand)
        {
            sCmd += FMT_STR;
            chk ^= FMT_STR;
            cmdBuff += chk;
            ByteConvert::Expand(cmdBuff, sCmd, 0x30);
        }
        else
        {
            sCmd += FMT_BIN;
            chk ^= FMT_BIN;
            cmdBuff += chk;
            sCmd += cmdBuff;
        }
    }
    /// 按照格式接收数据
    static bool RecvByFormat(IInteractiveTrans& dev, ByteBuilder& data, TCR_ERR& err, ByteBuilder* pCachedBuff = NULL)
    {
        ByteBuilder buff(64);
        if(pCachedBuff != NULL)
        {
            buff.Append(*pCachedBuff);
            pCachedBuff->Clear();
        }
        /// 前导码最小长度
        size_t CmdHeaderLength = 2;
        if(IInteractiveTransHelper::ReadAssert(dev, CmdHeaderLength, buff) != DevHelper::EnumSUCCESS)
        {
            return false;
        }
        
        if(buff[0] != 0x7E)
            return false;

        bool isExpand = (buff[1] == 0x53);
        if(isExpand)
        {
            CmdHeaderLength = 12;
        }
        else
        {
            CmdHeaderLength = 8;
        }
        if(IInteractiveTransHelper::ReadAssert(dev, CmdHeaderLength, buff) != DevHelper::EnumSUCCESS)
        {
            return false;
        }
        err = static_cast<TCR_ERR>(buff[3]);

        ByteBuilder lenBuff(8);
        if(isExpand)
        {
            ByteConvert::Fold(buff.SubArray(4, 8), lenBuff);
        }
        else
        {
            lenBuff = buff.SubArray(4, 4);
        }
        size_t datalen = 0;
        ByteConvert::ToObject(lenBuff, datalen);

        size_t count = CmdHeaderLength;
        if(isExpand)
        {
            count += 2 * datalen;
            // 校验和长度
            count += 2;
        }
        else
        {
            count += datalen;
            // 校验和长度
            count += 1;
        }
        if(IInteractiveTransHelper::ReadAssert(dev, count, buff) != DevHelper::EnumSUCCESS)
        {
            return false;
        }
        if(pCachedBuff != NULL)
        {
            pCachedBuff->Append(buff.SubArray(count));
        }
        // 截取数据
        if(isExpand)
        {
            ByteConvert::Fold(buff.SubArray(CmdHeaderLength, 2 * datalen), data);
        }
        else
        {
            data += buff.SubArray(CmdHeaderLength, datalen);
        }
        return true;
    }
    /// 将指纹数据保存成图片
    static bool ToBmp(const ByteArray& image, const char* filePath)
    {
        const size_t COLOR_TABLE_LENGTH = 256;
        const size_t nImgMaxSize = 152 * 200;

        ofstream fout;
        fout.open(filePath, ios::out | ios::binary);
        if(fout.fail())
            return false;

        ByteBuilder bmpBuff(1024);
        // BITMAPFILEHEADER
        DevCommand::FromAscii("424DF67600000000000036040000", bmpBuff);
        // BITMAPINFOHEADER
        DevCommand::FromAscii("2800000098000000C800000001000800000000000000000000000000000000000000000000000000", bmpBuff);
        // COLOR_TABLE
        for(int i = 0; i < COLOR_TABLE_LENGTH; i++)
        {
            bmpBuff += _itobyte(i);
            bmpBuff += _itobyte(i);
            bmpBuff += _itobyte(i);
            bmpBuff += static_cast<byte>(0x00);
        }
        fout.write(bmpBuff.GetString(), bmpBuff.GetLength());
        const char* pImage = image.GetString();
        for(size_t i = image.GetLength();i > 0; --i)
        {
            fout.write(pImage + i - 1, 1);
        }
        fout.close();

        return true;
    }
    //----------------------------------------------------- 
protected:
    //----------------------------------------------------- 
    /// 发送缓冲区
    ByteBuilder _sendBuffer;
    /// 接收缓冲区
    ByteBuilder _recvBuffer;
    /// 上次指令的ACK
    TCR_ERR _ack;
    //----------------------------------------------------- 
public:
    //----------------------------------------------------- 
    /// 交互指令
    bool TransCommand(byte cmd, const ByteArray& data, bool isExpand, ByteBuilder& recv)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "Cmd:<" << _hex(cmd) << ">\n";
        _log << "Arg:<";_log.WriteStream(data) << ">\n";
        _log << "IsExpand:<" << isExpand << ">\n");
        _ack = TCR_SUCC;
        ASSERT_Device();

        _sendBuffer.Clear();
        _recvBuffer.Clear();

        PackSendCmd(cmd, data, isExpand, _sendBuffer);
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(RecvByFormat(_pDev, recv, _ack), DeviceError::RecvErr);
        LOGGER(_log << "ACK:<" << static_cast<int>(_ack) << ">\n");
        ASSERT_FuncErrInfoRet(_ack == TCR_SUCC, DeviceError::DevReturnErr, TransErrToString(_ack));
        return _logRetValue(true);
    }
    /// 获取指纹图像结构
    bool GetFPRINFOHDR(ByteBuilder& data)
    {
        const byte CMD[] = { 0x04, 0x00, 0x00, 0x00 };
        return TransCommand(ORD_CAPT, ByteArray(CMD, 4), false, data);
    }
    /// 获取版本信息
    bool GetVersion(ByteBuilder& data)
    {
        const byte CMD[] = { 0x00 };
        return TransCommand(ORD_INFO, ByteArray(CMD, SizeOfArray(CMD)), false, data);
    }
    /**
     * @brief 获取模板
     * @date 2017-07-22 16:14
     * 
     * @param [out] data 获取到的模板数据
     * @param [in] isTest [default:false] 录取完模板后是否补测一枚指纹
     */
    bool GetTemplate(ByteBuilder& data, bool isTest = false)
    {
        byte CMD[] = { 0x00 };
        if(isTest)
        {
            CMD[0] = 0x01;
        }
        return TransCommand(ORD_REGI, ByteArray(CMD, SizeOfArray(CMD)), false, data);
    }
    /**
     * @brief 获取特征
     * @date 2017-07-16 22:34
     * 
     * @param [out] data 获取到的特征数据
     * @param [in] mode [default:0x02] 操作模式
     * - 模式:
     *  - 0x00 : 在库中1:N搜索不返回特征
     *  - 0x01 : 在库中1:N搜索并返回特征
     *  - 0x02 : 不搜索直接返回特征
     * .
     */
    bool GetFeature(ByteBuilder& data, byte mode = 0x02)
    {
        return TransCommand(ORD_VERI, ByteArray(&mode, 1), false, data);
    }
    /// 返回是否按了指纹
    bool IsCaptureFinger()
    {
        const byte CMD[] = { 0x05, 0x00, 0x00, 0x00 };
        ByteBuilder data(8);
        return TransCommand(ORD_CAPT, ByteArray(CMD, SizeOfArray(CMD)), false, data);
    }
    /**
     * @brief 获取指纹图片
     * @date 2017-07-16 11:21
     * 
     * @param [out] data 获取到的数据
     * @param [in] imgFlag [default:0x0B] 图像格式
     * - 格式:
     *  - 0x00 不返回图像
     *  - 0x01 原始灰度图
     *  - 0x0B 原大灰度
     *  - 0x0C 四分之一灰度
     *  - 0x0D 灰度图二值化
     *  - 0x0E 四分之一灰度图二值化
     * .
     */
    bool GetImage(ByteBuilder& data, byte imgFlag = 0x0B)
    {
        byte CMD[] = { 0x00, 0x01, 0x00, 0x00 };
        CMD[2] = imgFlag;
        return TransCommand(ORD_CAPT, ByteArray(CMD, 4), false, data);
    }
    /// 返回上次错误码
    inline TCR_ERR GetAck() const
    {
        return _ack;
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
} // namespace finger
} // namespace application 
} // namespace zhou_yb 
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_TC_FINGERDEVADAPTER_H_
//========================================================= 